package fr.resasante.ejb.service.helper;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.naming.NamingException;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.jboss.logging.Logger;

import fr.resasante.ejb.dto.User;
import fr.resasante.ejb.dto.UserRole;
import fr.resasante.ejb.util.HibernateUtil;
import fr.resasante.ejb.utils.EjbResponse;
import fr.resasante.utils.CommonUtils;

/**
 * Class for getting system properties
 * 
 * @author GEFFROY-DE-VILLEBLAN
 * 
 */
public final class UserCreateHelper {
	/**
	 * Professional type
	 */
	public static final String PROFESSIONAL = "Professionnel";

	/**
	 * Patient type
	 */
	public static final String PATIENT = "Patient";

	/**
	 * EJb response
	 */
	private EjbResponse ejbResponse = null;

	/**
	 * class instance
	 */
	private static volatile UserCreateHelper instance = null;

	/**
	 * Logger for the class
	 */
	private static final Logger logger = Logger.getLogger(UserCreateHelper.class.getName());

	/**
	 * Method returning a Singleton class instance
	 * 
	 * @return Singleton instance
	 * @throws NamingException
	 */
	public final static UserCreateHelper getInstance() {
		if (UserCreateHelper.instance == null) {
			synchronized (UserCreateHelper.class) {
				if (UserCreateHelper.instance == null) {
					UserCreateHelper.instance = new UserCreateHelper();
				}
			}
		}
		return UserCreateHelper.instance;
	}

	/**
	 * The user object
	 */
	private User user = null;

	/**
	 * Constructor
	 */
	private UserCreateHelper() {
		super();
	}

	/**
	 * Add a patient
	 * 
	 * @param userIncoming
	 *            The user object
	 * @return If everything goes fine
	 */
	public EjbResponse addPatient(User userIncoming) {
		ejbResponse = new EjbResponse();
		prepareUser(userIncoming, PATIENT);
		createUser(PATIENT);
		return ejbResponse;
	}

	/**
	 * Add a professional
	 * 
	 * @param userIncoming
	 *            The user object
	 * @return If everything goes fine
	 */
	public EjbResponse addProfessional(User userIncoming) {
		ejbResponse = new EjbResponse();
		prepareUser(userIncoming, PROFESSIONAL);
		createUser(PROFESSIONAL);
		return ejbResponse;
	}

	/**
	 * Create the user in database
	 * 
	 * @param type
	 *            The user type
	 */
	private void createUser(String type) {
		final Session session = HibernateUtil.getSession();
		if (UserViewHelper.getInstance().usernameExists(user.getUsername())) {
			ejbResponse.addError("error.username.already.exists");
		}
		if (UserViewHelper.getInstance().emailExists(user.getMail())) {
			ejbResponse.addError("error.email.already.exists");
		}
		if (ejbResponse.getErrors().isEmpty()) {
			Transaction transaction = null;
			try {
				transaction = session.beginTransaction();
				session.save(user);
				transaction.commit();
				logger.info(type + " " + user.getUsername() + " successfully created !");
			} catch (final RuntimeException e) {
				logger.error(CommonUtils.getInstance().getStackTrace(e));
				if (transaction != null) {
					transaction.rollback();
				}
				ejbResponse.addError("error.from.database");
			} finally {
				session.close();
			}
		}
	}

	/**
	 * Prepare the object user with the passed parameters
	 * 
	 * @param userIncoming
	 *            The passed user object
	 * @param type
	 *            The user type
	 */
	private void prepareUser(User userIncoming, String type) {
		logger.info("Starting the creation of the " + type.toLowerCase() + " "
				+ userIncoming.getUsername());
		user = userIncoming;
		// Modify the user
		setUserType(type);
		setRole();
		try {
			setNullValues();
		} catch (final Exception e) {
			logger.error(CommonUtils.getInstance().getStackTrace(e));
			ejbResponse.addError("error.calling.method");
		}
		setPassword();
		setSalt();
	}

	/**
	 * Set the empty values in place of null
	 * 
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws InvocationTargetException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 */
	public void setNullValues() throws SecurityException, NoSuchMethodException,
			IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		// Patient
		String[] methods = null;
		if (PATIENT.equals(user.getUserType())) {
			methods = new String[] { "Agreement", "CorporateName", "Job" };
		} else {
			methods = new String[] { "RppsNumber", "AdeliNumber", "Title", "Speciality",
					"PostOfficeBox", "GsmPhone", "Fax" };
		}
		for (final String method : methods) {
			final Method getMethod = User.class.getDeclaredMethod("get" + method);
			final Method setMethod = User.class
					.getDeclaredMethod("set" + method, String.class);
			final String value = (String) getMethod.invoke(user);
			if (PATIENT.equals(user.getUserType()) && value == null) {
				if ("Agreement".equals(method)) {
					setMethod.invoke(user, "NA");
				} else {
					setMethod.invoke(user, "");
				}
			} else if (PROFESSIONAL.equals(user.getUserType()) && "".equals(value)) {
				setMethod.invoke(user, (String) null);
			}
		}
	}

	/**
	 * Set an encrypted password
	 */
	public void setPassword() {
		// Password encryption
		user.setPassword(CommonUtils.getInstance().encryptPassword(user.getPassword()));
	}

	/**
	 * Set the role for the user
	 */
	public void setRole() {
		final UserRole userRole = new UserRole();
		userRole.setAuthority("ROLE_USER");
		userRole.setUser(user);
		user.getUserRoles().add(userRole);
	}

	/**
	 * Set the salt string for the user
	 */
	public void setSalt() {
		user.setSalt(CommonUtils.getInstance().generateSalt());
	}

	/**
	 * Set the user type
	 * 
	 * @param type
	 *            The user type
	 */
	public void setUserType(String type) {
		user.setUserType(type);
	}
}